package com.hooya.fa.eu.biz.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hooya.fa.eu.api.dto.PlInvoiceFlushDTO;
import com.hooya.fa.eu.api.dto.PlInvoiceQueryDTO;
import com.hooya.fa.eu.api.feign.RemoteThirdPtService;
import com.hooya.fa.eu.biz.dao.entity.PlInvoice;
import com.hooya.fa.eu.biz.dao.entity.PlInvoiceRecord;
import com.hooya.fa.eu.biz.dao.manager.PlInvoiceManager;
import com.hooya.fa.eu.biz.dao.mapper.PlInvoiceMapper;
import com.hooya.fa.eu.biz.dao.repository.PlInvoiceRecordRepository;
import com.hooya.fa.eu.biz.dao.repository.PlInvoiceRepository;
import com.hooya.fa.eu.biz.service.PLInvoiceService;
import com.hooya.integrated.common.other.data.DataMethod;
import com.hooya.integrated.common.util.times.DateExtUtil;
import com.hooya.integrated.thirdpt.api.dto.*;
import com.hooya.integrated.thirdpt.api.vo.InvoicingResultVO;
import com.xxl.job.core.context.XxlJobHelper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * @Description 波兰发票服务
 * @Author : Zongz
 * @Copyright Copyright (c) 豪雅集团
 * @Create 2023/12/5
 * @Version 1.0.0
 */
@Slf4j
@Service
@DS("fa_eu")
@RefreshScope
@AllArgsConstructor
public class PLInvoiceServiceImpl extends ServiceImpl<PlInvoiceMapper, PlInvoice> implements PLInvoiceService {

    private final static Integer INTERVAL_TIME = 14;

    private final TransactionTemplate transactionTemplate;

    private final PlInvoiceRepository plInvoiceRepository;

    private final PlInvoiceRecordRepository plInvoiceRecordRepository;

    private final PlInvoiceManager plInvoiceManager;

    private final RemoteThirdPtService remoteThirdPtService;

    @Override
    public int addPLInvoice() {
        List<PlInvoice> list = plInvoiceRepository.getNeedPLInvoicingData();
        if (CollectionUtils.isEmpty(list)) {
            return 0;
        }
        List<String> invoiceIds = list.stream().map(PlInvoice::getInvoiceId).collect(Collectors.toList());
        List<PlInvoiceRecord> recordList = plInvoiceRecordRepository.getListByInvoiceIds(invoiceIds);
        if (CollectionUtils.isEmpty(recordList)) {
            XxlJobHelper.log("发票无明细，数据存在问题");
            log.error("发票无明细，数据存在问题");
            return 0;
        }
        Map<String, List<PlInvoiceRecord>> recordMap = recordList.stream()
                .collect(Collectors.groupingBy(PlInvoiceRecord::getInvoiceId));
        //回写对象----updateData：回写状态；updateRecordData：回写PL信息
        List<PlInvoice> updateInvoiceData = new LinkedList<>();
        List<PlInvoiceRecord> updateRecordData = new LinkedList<>();
        //回滚PL（即删除PL）
        List<Integer> plIds = new LinkedList<>();
        //记录被分段position的plId
        Set<Integer> updateErrorPLId = new HashSet<>();
        list.forEach(l -> {
            //payment_to时间
            LocalDate originalDate = LocalDate.parse(l.getSellDate());
            LocalDate newDate = originalDate.plusDays(INTERVAL_TIME);
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

            log.info("本次新增的发票号:" + l.getInvoiceNumber());
            XxlJobHelper.log("本次新增的发票号:" + l.getInvoiceNumber());
            //新增开票信息
            InvoiceDTO add = new InvoiceDTO();
            InvoiceInfoDTO infoDTO = InvoiceInfoDTO.builder().kind(l.getInvoiceType()).lang("en").department_id(1047023)
                    .number(l.getInvoiceNumber()).sell_date(l.getSellDate()).issue_date(l.getSellDate())
                    .payment_to(newDate.format(formatter)).seller_name(l.getSellerName()).seller_country(l.getSellerCountry())
                    .seller_street(l.getSellerStreet()).seller_city(l.getSellerCity()).seller_tax_no(l.getSellerTaxNo())
                    .currency("EUR").buyer_name(l.getBuyerName()).buyer_country(l.getBuyerCountry()).buyer_city(l.getBuyerCity())
                    .buyer_street(l.getBuyerStreet()).buyer_tax_no(l.getBuyerTaxNo()).build();
            if (l.getSellerTaxNo().startsWith("IT")) {
                infoDTO.setDescription(l.getDescription());
                infoDTO.setExchange_currency("PLN");
            }
            if (l.getSellerTaxNo().startsWith("PL") && l.getBuyerTaxNo().startsWith("PL") && l.getTax() != 0) {
                infoDTO.setExchange_currency("PLN");
            }
            List<InvoicePosition> addPositions = new LinkedList<>();
            infoDTO.setPositions(addPositions);
            add.setInvoice(infoDTO);

            List<PlInvoiceRecord> records = recordMap.get(l.getInvoiceId());
            //明细太多需要进行数据分段,以600为跨度分，共分partialLimit次
            //在新增更新过程中，失败了直接跳过此次发票执行，放置下次处理
            int partialLimit = records.size() / 600 + 1;
            if (partialLimit > 1) {
                //分割数据
                List<List<PlInvoiceRecord>> splitList = DataMethod.splitData(records, 600);
                Integer plId = 0;
                for (int i = 0; i < splitList.size(); i++) {
                    if (i == 0) {
                        plInvoiceManager.dealWithInvoiceRecord(splitList.get(i), addPositions, l, true);
                        InvoicingResultVO invoicingRes = plInvoiceManager.addPLInvoiceInfo(add, plIds);
                        if (Objects.isNull(invoicingRes)) {
                            return;
                        }
                        plId = invoicingRes.getId();
                        l.setPlInvoiceId(plId);
                        l.setClientId(invoicingRes.getClient_id());
                        plInvoiceManager.writeBackRecord(invoicingRes, splitList.get(i), updateRecordData);
                    } else {
                        InvoiceUpdateDTO update = new InvoiceUpdateDTO();
                        List<InvoicePosition> updatePositions = new LinkedList<>();
                        update.setInvoice(InvoiceInfoUpdateDTO.builder().positions(updatePositions).build());
                        plInvoiceManager.dealWithInvoiceRecord(splitList.get(i), updatePositions, l, false);
                        InvoicingResultVO invoicingRes = plInvoiceManager.updatePLInvoiceInfo(plId, update);
                        if (Objects.isNull(invoicingRes)) {
                            updateErrorPLId.add(plId);
                            return;
                        }
                        plInvoiceManager.writeBackRecord(invoicingRes, splitList.get(i), updateRecordData);
                    }
                }
            } else {
                plInvoiceManager.dealWithInvoiceRecord(records, addPositions, l, true);
                InvoicingResultVO invoicingRes = plInvoiceManager.addPLInvoiceInfo(add, plIds);
                if (Objects.isNull(invoicingRes)) {
                    return;
                }
                plInvoiceManager.writeBackRecord(invoicingRes, records, updateRecordData);
                l.setPlInvoiceId(invoicingRes.getId());
                l.setClientId(invoicingRes.getClient_id());
            }
            l.setSyncStatus(2);
            updateInvoiceData.add(l);
        });
        //说明分段增加明细的时候失败了，并且删除对应pLId
        if (!CollectionUtils.isEmpty(updateErrorPLId)) {
            plInvoiceManager.batchDeletePlInvoice(new ArrayList<>(updateErrorPLId));
        }
        if (!CollectionUtils.isEmpty(updateRecordData)) {
            try {
                transactionTemplate.execute(status -> {
                    //更新updateData
//                    log.info(Arrays.toString(updateRecordData.stream().map(PlInvoiceRecord::getInvoiceId).toArray()));
                    if (!CollectionUtils.isEmpty(updateErrorPLId)) {
                        plInvoiceRecordRepository.batchUpdate(updateRecordData.stream()
                                .filter(l -> !updateErrorPLId.contains(l.getPlInvoiceId())).collect(Collectors.toList()));
                    } else {
                        plInvoiceRecordRepository.batchUpdate(updateRecordData);
                    }
                    plInvoiceRepository.batchUpdate(updateInvoiceData);
                    return true;
                });
            } catch (Exception e) {
                plInvoiceManager.batchDeletePlInvoice(plIds);
                log.error(e.getMessage());
                XxlJobHelper.log("添加发票失败，失败原因：" + e.getMessage());
                throw new RuntimeException("添加发票失败，失败原因：" + e.getMessage());
            }
        }
        return 0;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addCorrectionInvoice(String startDate, String endDate) {
        //修正发票信息
        List<PlInvoice> correctionList = plInvoiceRepository.getCorrectionInvoiceByDate(startDate, endDate);
        if (CollectionUtils.isEmpty(correctionList)) {
            return;
        }
        List<String> correctionIds = correctionList.stream().map(PlInvoice::getInvoiceId).collect(Collectors.toList());
        List<PlInvoiceRecord> correctionRecordList = plInvoiceRecordRepository.getListByInvoiceIds(correctionIds);
        Map<String, List<PlInvoiceRecord>> correctionRecordMap = correctionRecordList.stream()
                .collect(Collectors.groupingBy(PlInvoiceRecord::getInvoiceId));
        Map<String, PlInvoice> correctionMap = correctionList.stream()
                .collect(Collectors.toMap(l -> l.getInvoiceNumber().split("/")[1], d -> d));
        //原始发票信息
        List<String> originInvoiceNumberList = new ArrayList<>(correctionMap.keySet());
        List<PlInvoice> originList = plInvoiceRepository.getOriginInvoiceByCondition(originInvoiceNumberList, startDate, endDate);
        List<String> originIds = originList.stream().map(PlInvoice::getInvoiceId).collect(Collectors.toList());
        List<PlInvoiceRecord> originRecordList = plInvoiceRecordRepository.getListByInvoiceIds(originIds);
        Map<String, List<PlInvoiceRecord>> originRecordMap = originRecordList.stream()
                .collect(Collectors.groupingBy(PlInvoiceRecord::getInvoiceId));
        //更新
        List<PlInvoice> updateInvoice = new LinkedList<>();
        //回滚PL（即删除PL）
        List<Integer> deletePlIds = new LinkedList<>();

        //处理数据
        for (PlInvoice origin : originList) {
            PlInvoice correction = correctionMap.get(origin.getInvoiceNumber());
            log.info("本次新增的发票号:" + correction.getInvoiceNumber());
            XxlJobHelper.log("本次新增的发票号:" + correction.getInvoiceNumber());
            //修正开票信息
            CorrectionInvoiceDTO add = new CorrectionInvoiceDTO();
            CorrectionInvoiceInfoDTO infoDTO = CorrectionInvoiceInfoDTO.builder().kind(correction.getInvoiceType())
                    .lang("en").number(correction.getInvoiceNumber()).invoice_id(origin.getPlInvoiceId())
                    .from_invoice_id(origin.getPlInvoiceId()).client_id(origin.getClientId()).currency("EUR")
                    .correction_reason("Zła ilość").build();
            List<CorrectionInvoicePositionDTO> addPositions = new LinkedList<>();
            infoDTO.setPositions(addPositions);
            add.setInvoice(infoDTO);
            //修正明细处理
            List<PlInvoiceRecord> correctionRecord = correctionRecordMap.get(correction.getInvoiceId());
            List<PlInvoiceRecord> originRecord = originRecordMap.get(origin.getInvoiceId());
            Map<String, PlInvoiceRecord> correctionSkuMap = correctionRecord.stream()
                    .collect(Collectors.toMap(PlInvoiceRecord::getSku, i -> i));
            Map<String, PlInvoiceRecord> originSkuMap = originRecord.stream()
                    .collect(Collectors.toMap(PlInvoiceRecord::getSku, i -> i));
            Set<String> skuList = new HashSet<>();
            skuList.addAll(correctionRecord.stream().map(PlInvoiceRecord::getSku).collect(Collectors.toList()));
            skuList.addAll(originRecord.stream().map(PlInvoiceRecord::getSku).collect(Collectors.toList()));
            List<List<String>> spanSkuList = DataMethod.splitData(skuList, 600);
            //运费
            plInvoiceManager.addDeliveryCharge(origin, correction, addPositions);
            //first
            plInvoiceManager.dealWithCorrectionInvoicePosition(correction,
                    spanSkuList.get(0), originSkuMap, correctionSkuMap, addPositions);
            InvoicingResultVO invoicingRes = plInvoiceManager.addCorrectionInvoiceInfo(add);
            if (Objects.isNull(invoicingRes)) {
                continue;
            }
            Integer plId = invoicingRes.getId();
            Integer clientId = invoicingRes.getClient_id();
            //后续sku
            AtomicBoolean flag = new AtomicBoolean(true);
            if (spanSkuList.size() > 1) {
                for (int i = 1; i < spanSkuList.size(); i++) {
                    CorrectionInvoiceUpdateDTO updateDTO = new CorrectionInvoiceUpdateDTO();
                    List<CorrectionInvoicePositionDTO> updatePositionList = plInvoiceManager
                            .dealWithCorrectionInvoicePosition(correction, spanSkuList.get(i),
                                    originSkuMap, correctionSkuMap, addPositions);
                    CorrectionInvoiceInfoUpdateDTO updateInfoDTO = new CorrectionInvoiceInfoUpdateDTO();
                    updateDTO.setInvoice(updateInfoDTO);
                    updateInfoDTO.setPositions(updatePositionList);
                    InvoicingResultVO updated = plInvoiceManager.updateCorrectionInvoiceInfo(plId, updateDTO);
                    if (Objects.isNull(updated)) {
                        deletePlIds.add(plId);
                        flag.set(false);
                        break;
                    }
                }
            }
            if (flag.get()) {
                correction.setPlInvoiceId(plId);
                correction.setSyncStatus(2);
                correction.setClientId(clientId);
                updateInvoice.add(correction);
            }
        }
        //更新失败得需要删除
        if (!CollectionUtils.isEmpty(deletePlIds)) {
            plInvoiceManager.batchDeletePlInvoice(deletePlIds);
        }
        if (!CollectionUtils.isEmpty(updateInvoice)) {
            plInvoiceRepository.batchUpdate(updateInvoice);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void flushInfo() {
        List<PlInvoice> list = plInvoiceRepository.getFinishedList();
        list.forEach(l -> {
            InvoiceFlushDTO flushDTO = new InvoiceFlushDTO();
            InvoiceInfoFlushDTO info = new InvoiceInfoFlushDTO();
            info.setCurrency("EUR");
            info.setKind(l.getInvoiceType());
            info.setBuyer_country(l.getBuyerCountry());
            info.setBuyer_city(l.getBuyerCity());
            info.setBuyer_street(l.getBuyerStreet());
            info.setSeller_city(l.getSellerCity());
            info.setSeller_country(l.getSellerCountry());
            if (l.getSellerName().startsWith("Costway")) {
                info.setSeller_name("Costway Sp. z o.o.");
            } else {
                info.setSeller_name(l.getSellerName());
            }
            flushDTO.setInvoice(info);
            try {
                log.info("pl id：" + l.getPlInvoiceId());
                //使用时解开注释
//                remotePLInvoiceService.flushInvoiceInfo(l.getPlInvoiceId(), flushDTO);
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        });
    }

    @Override
    public String temporaryFlushInfo(PlInvoiceFlushDTO dto) {
        StringBuffer sb = new StringBuffer();
        if (!CollectionUtils.isEmpty(dto.getPlInvoiceIds())) {
            InvoiceFlushDTO flushDTO = new InvoiceFlushDTO();
            InvoiceInfoFlushDTO infoDTO = InvoiceInfoFlushDTO.builder().kind(dto.getInvoiceType())
                    .buyer_name(dto.getBuyerName()).buyer_tax_no(dto.getBuyerTaxNo()).buyer_street(dto.getBuyerName())
                    .buyer_city(dto.getBuyerCity()).buyer_country(dto.getBuyerCountry())
                    .seller_name(dto.getSellerName()).sell_date(dto.getSellDate()).seller_tax_no(dto.getSellerTaxNo())
                    .seller_city(dto.getSellerCity()).seller_country(dto.getSellerCountry()).currency(dto.getCurrency())
                    .build();
            flushDTO.setInvoice(infoDTO);
            dto.getPlInvoiceIds().forEach(id -> {
                try {
                    remoteThirdPtService.update(id, flushDTO);
                } catch (Exception e) {
                    String errInfo = "pl id:" + id + "，错误信息：" + e.getMessage();
                    log.error(errInfo);
                    sb.append(errInfo).append(";");
                }
            });
        }
        return sb.toString();
    }

    @Deprecated
    private void flushInvoiceType(List<PlInvoice> plInvoices) {
        if (!CollectionUtils.isEmpty(plInvoices)) {
            List<Integer> plIds = plInvoices.stream().map(PlInvoice::getPlInvoiceId).collect(Collectors.toList());
            TempFlushDTO dto = new TempFlushDTO();
            TempInvoiceInfoDTO temp = TempInvoiceInfoDTO.builder().kind("proforma").build();
            dto.setInvoice(temp);
            plIds.forEach(id -> {
                try {
                    log.info("pl id:" + id);
                    //使用时解开注释
//                    remotePLInvoiceService.flushInvoiceInfo(id, dto);
                } catch (Exception e) {
                    log.error("错误信息：" + e.getMessage());
                }
            });
        }
    }

    /**
     * 刷pl_invoice_dtl的id
     */
    @Deprecated
    public void flushPLInvoiceDtl() {
        //------------------------ 回写公司地址 ------------------------
        List<PlInvoice> plInvoices = plInvoiceRepository.getList();
        Map<Integer, PlInvoice> plInvoiceMap = plInvoices.stream()
                .collect(Collectors.toMap(PlInvoice::getPlInvoiceId, i -> i));
        plInvoiceMap.forEach((id, l) -> {
            try {
                log.info("pl id:" + id);
                InvoicingResultVO invoice = plInvoiceManager.getInvoiceById(id);
                InvoiceFlushDTO dto = new InvoiceFlushDTO();
                InvoiceInfoFlushDTO flushDTO = InvoiceInfoFlushDTO.builder().kind(l.getInvoiceType())
                        .buyer_country(l.getBuyerCountry()).buyer_city(l.getBuyerCity())
                        .buyer_street(l.getBuyerStreet()).seller_country(l.getSellerCountry())
                        .seller_city(l.getSellerCity()).seller_street(l.getSellerStreet())
                        .currency("EUR").seller_name(invoice.getSeller_name()).build();
                dto.setInvoice(flushDTO);
                //使用时解开注释
//                remotePLInvoiceService.flushInvoiceInfo(id, dto);
            } catch (Exception e) {
                log.error("发票类型：" + l.getInvoiceType() + ".错误信息：" + e.getMessage());
            }
        });
    }

    @Override
    public void batchDeleteByPlIds(String date) {
        if (StringUtils.isBlank(date)) {
            date = LocalDate.now().toString();
        }
        List<PlInvoice> list = plInvoiceRepository.getByCreateTime(date);
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<Integer> plInvoiceIds = list.stream().map(PlInvoice::getPlInvoiceId).collect(Collectors.toList());
        try {
            plInvoiceManager.batchDeletePlInvoice(plInvoiceIds);
        } catch (Exception e) {
            log.info(e.getMessage());
        }
    }

    @Override
    public void batchDeleteByNumber(String date) {
        if (StringUtils.isBlank(date)) {
            date = LocalDate.now().toString();
        }
        List<PlInvoice> list = plInvoiceRepository.getByCreateTime(date);
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (PlInvoice invoice : list) {
            plInvoiceManager.deletePlInvoiceByNumber(invoice.getInvoiceNumber());
        }
    }

    @Override
    @DSTransactional
    public void writeBackClientId(String date) {
        String endDate = DateExtUtil.getNextMonthFirstDay(LocalDate.parse(date), 1);
        List<PlInvoice> list = plInvoiceRepository.getPlInvoicedListByDate(date, endDate);
        List<Integer> ids = list.stream().map(PlInvoice::getPlInvoiceId).collect(Collectors.toList());
        List<InvoicingResultVO> invoiceList = plInvoiceManager.getInvoiceList(ids);
        Map<Integer, Integer> clientIdMap = invoiceList.stream()
                .collect(Collectors.toMap(InvoicingResultVO::getId, InvoicingResultVO::getClient_id));
        for (PlInvoice p : list) {
            Integer clientId = clientIdMap.get(p.getPlInvoiceId());
            p.setClientId(clientId);
        }
        plInvoiceRepository.batchUpdate(list);
    }

    @Override
    public List<PlInvoice> getFlushInfo(PlInvoiceQueryDTO query) {
        return plInvoiceRepository.getListByCondition(query);
    }

}
